package hollycrm.com.libuip.internal;

import android.support.annotation.RestrictTo;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

import hollycrm.com.libuip.io.UIPBufferReader;
import hollycrm.com.libuip.socket.SocketClient;

/**
 * Created by aber on 2017/1/17.
 * Using the base Java socket api implement the {@link SocketClient}
 *
 * {@hide}
 */
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class JavaSocketClient implements SocketClient {

    private static final String TAG = JavaSocketClient.class.getSimpleName();

    private static final int LOG_SEND = 0x01;
    private static final int LOG_RECEIVE = 0x02;
    private static final int LOG_ERROR = 0x04;
    private static final int LOG_ALL = LOG_ERROR | LOG_RECEIVE | LOG_SEND;

    private static final char endChar = '$';
    private String host;
    private int port;
    private PrintWriter out;
    private UIPBufferReader in;
    private SocketClient.Callback callback;
    private volatile boolean mRun;
    @SuppressWarnings("FieldCanBeLocal")
    private String incomingMessage;

    private int logLevel = LOG_ALL;

    public JavaSocketClient(String host, int port,SocketClient.Callback callback) {
        this.host = host;
        this.port = port;
        this.callback = callback;
    }

    /**
     * Log Level indication, determine which log info
     * should be print or other logic: example override
     * @see {@link #LOG_ERROR,#LOG_SEND,#LOG_RECEIVE}
     * @see {@link #LOG_ALL}
     *
     * example:
     * <code>
     *     int level = LOG_ERROR | LOG_SEND
     *     // the ERROR and SEND log will be actived
     * </code>
     * */
    public void setLogLevel(int level) {
        this.logLevel = level;
    }

    @Override
    public void send(String message) {
        sendMessage(message,endChar);
    }

    @Override
    public void sendHeart(String message) {

    }

    @Override
    public void setCallback(SocketClient.Callback callback) {
        this.callback = callback;
    }

    /**
     * if we disconnect the socket connection loop will be close
     * */
    public void disconnect() {
        mRun = false;
        synchronized (this) {
            if (callback != null) callback.onDisconnected();
        }
    }

    public boolean isRunning() {
        return mRun;
    }

    @Override
    public void setHeartMessage(String message) {

    }

    /**
     * Start the TCP connection
     * */
    @WorkerThread
    public final void connect() {
        mRun = true;
        if (callback != null) callback.onConnecting();
        try {
            InetAddress serverAddress = InetAddress.getByName(host);
            Socket socket = new Socket(serverAddress,port);
            socket.setKeepAlive(true);

            try {
                in = new UIPBufferReader(new InputStreamReader(socket.getInputStream()));
                out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));

                if (callback != null) callback.onConnected();
                while (mRun) {
                    incomingMessage = in.readLine$();
                    if (!TextUtils.isEmpty(incomingMessage)) {
                        dispatchIncomingMessage(incomingMessage);
                    }
                    incomingMessage = null;
                }
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
                out.flush();
                out.close();
                try {
                    in.close();
                    socket.close();
                    if (callback != null)
                        callback.onDisconnected();
                }catch (IOException e) {
                    if (callback != null) callback.onError(e);
                }
            }
        }catch (Exception e) {
            if (callback != null) callback.onError(e);
        }
    }
    /**
     * send a message and end with a spacial char <code>eC</code> which indicate message is ended
     * */
    private void sendMessage(String message,char eC) {
        if (out != null && !out.checkError()) {
            out.write(message);
            out.write(eC);
            out.flush();
        }
    }
    /**
     * dispatching incoming message
     * @param message incoming message
     * */
    private void dispatchIncomingMessage(String message) {
        if (callback != null)
            callback.onReceived(message);
    }
}
